home *** CD-ROM | disk | FTP | other *** search
- _STRUCTURED PROGRAMMING COLUMN_
- by Jeff Duntemann
-
- [LISTING ONE]
-
- {------------------------------------------------------------------------}
- { INTTERM }
- { by Jeff Duntemann }
- { Turbo Pascal V5.0 or later }
- { Last update 6/2/91 }
- { This is an interrupt-driven "dumb terminal" program for the PC. It }
- { the use of Turbo Pascal's INTERRUPT procedures, and in a lesser fashion}
- { the use of serial port hardware. It can be set to use either COM1: or }
- { COM2: by setting the COMPORT constant to 1 (for COM1) or 2 (for COM2) }
- { as appropriate and recompiling. }
- {------------------------------------------------------------------------}
-
- PROGRAM INTTERM;
-
- USES DOS,CRT;
-
- CONST
- COMPORT = 2; { 1 = COM1: 2 = COM2: }
- COMINT = 13-COMPORT; { 12 = COM1: (IRQ4) 11 = COM2: (IRQ3) }
- COMBASE = $2F8;
- PORTBASE = COMBASE OR (COMPORT SHL 8); { $3F8 for COM1: $2F8 for COM2: }
-
- { 8250 control registers, masks, etc. }
- RBR = PORTBASE; { 8250 Receive Buffer Register }
- THR = PORTBASE; { 8250 Transmit Holding Register }
- LCR = PORTBASE + 3; { 8250 Line Control Register }
- IER = PORTBASE + 1; { 8250 Interrupt Enable Register }
- MCR = PORTBASE + 4; { 8250 Modem Control Register }
- LSR = PORTBASE + 5; { 8250 Line Status Register }
- DLL = PORTBASE; { 8250 Divisor Latch LSB }
- DLM = PORTBASE + 1; { 8250 Divisor Latch MSB }
- DLAB = $80; { 8250 Divisor Latch Access Bit }
-
- BAUD300 = 384; { Value for 300 baud operation }
- BAUD1200 = 96; { Value for 1200 baud operation }
- NOPARITY = 0; { Comm format value for no parity }
- BITS8 = $03; { Comm format value for 8 bits }
- DTR = $01; { Value for Data Terminal Ready }
- RTS = $02; { value for Ready To Send }
- OUT2 = $08; { Bit that enables adapter interrupts }
- BUFFSIZE = 1023;
-
- { 8259 control registers, masks, etc. }
- OCW1 = $21; { 8259 Operation Control Word 1 }
- OCW2 = $20; { 8259 Operation Control Word 2 }
- { The 8259 mask bit is calculated depending on }
- { which serial port is used... }
- { $10 for COM1: (IRQ4); $08 for COM2: (IRQ3): }
- IRQBIT = $20 SHR COMPORT;
-
- TYPE
- CircularBuffer = ARRAY[0..BUFFSIZE] OF Char; { Input buffer }
- VAR
- Quit : Boolean; { Flag for exiting the program }
- HiBaud : Boolean; { True if 1200 baud is being used }
- KeyChar : Char; { Character from keyboard }
- CommChar : Char; { Character from the comm port }
- Divisor : Word; { Divisor value for setting baud rate }
- Clearit : Byte; { Dummy variable }
- Buffer : CircularBuffer; { Our incoming character buffer }
- LastRead, { Index of the last character read }
- LastSaved : Integer; { Index of the last character stored }
- NoShow : SET OF Char; { Don't show characters set }
- OldVector : Pointer; { Global storage slot for the old }
- { interrupt vector }
- PROCEDURE EnableInterrupts;
- INLINE($FB);
- {->>>>Incoming (Interrupt Service Routine)<<<<--------------------------------}
- { This is the ISR (interrupt Service Routine) for comm ports. DO NOT call this}
- { routine directly; you'll crash hard. The only way Incoming takes control is }
- { when a character coming in from modem triggers a hardware interrupt from }
- { serial port chip, the 8250 UART. Note that the register pseudo-parameters }
- { are not needed here, and you could omit them. However, omitting them doesn't}
- { really get you any more speed or reliability. }
- {-----------------------------------------------------------------------------}
-
- PROCEDURE Incoming(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : Word);
- INTERRUPT;
- BEGIN
- { First we have to enable interrupts *at the CPU* during the ISR: }
- EnableInterrupts;
- { The first "real work" we do is either wrap or increment the index of the }
- { last character saved. If index is "topped out" at buffer size (here, }
- { 1023) we force it to zero. This makes the 1024-byte buffer "circular," }
- { in that once the index hits the end, it rolls over to beginning again. }
- IF LastSaved >= BUFFSIZE THEN LastSaved := 0 ELSE Inc(LastSaved);
-
- { Next, we read the actual incoming character from the serial port's}
- { one-byte holding buffer: }
- Buffer[LastSaved] := Char(Port[RBR]);
-
- { Finally, we must send a control byte to the 8259 interrupt }
- { controller, telling it that the interrupt is finished: }
- Port[OCW2] := $20; { Send EOI byte to 8259 }
- END;
-
- {$F+}
- PROCEDURE IntTermExitProc;
- BEGIN
- Port[IER] := 0; { Disable interrupts at 8250 }
- Port[OCW1] := Port[OCW1] OR IRQBIT; { Disable comm int at 8259 }
- Port[MCR] := 0; { Bring the comm line down }
- SetIntVec(COMINT,OldVector); { Restore previously saved vector }
- END;
- {$F-}
-
- PROCEDURE SetupSerialPort;
- BEGIN
- LastRead := 0; { Initialize the circular buffer pointers }
- LastSaved := 0;
-
- Port[IER] := 0; { Disable 8250 interrupts while setting them up }
-
- GetIntVec(ComInt,OldVector); { Save old interrupt vector }
- ExitProc := @IntTermExitProc; { Hook exit proc into chain }
- SetIntVec(ComInt,@Incoming); { Put ISR address into vector table }
-
- Port[LCR] := Port[LCR] OR DLAB; { Set up 8250 to set baud rate }
- Port[DLL] := Lo(Divisor); { Set baud rate divisor }
- Port[DLM] := Hi(Divisor);
- Port[LCR] := BITS8 OR NOPARITY; { Set word length and parity }
- Port[MCR] := DTR OR RTS OR OUT2; { Enable adapter, DTR, & RTS }
- Port[OCW1] := Port[OCW1] AND (NOT IRQBIT); { Turn on 8259 comm ints }
- Clearit := Port[RBR]; { Clear any garbage from RBR }
- Clearit := Port[LSR]; { Clear any garbage from LSR }
- Port[IER] := $01; { Enable 8250 interrupt on received character }
- END;
-
- FUNCTION InStat : Boolean;
- BEGIN
- IF LastSaved <> LastRead THEN InStat := True
- ELSE InStat := False;
- END;
-
- FUNCTION InChar : Char; { Bring in the next character }
- BEGIN
- IF LastRead >= BUFFSIZE THEN LastRead := 0
- ELSE Inc(LastRead);
- InChar := Buffer[LastRead];
- END;
-
- PROCEDURE OutChar(Ch : Char); { Send a character to the comm port }
- BEGIN
- Port[THR] := Byte(Ch) { Put character ito Transmit Holding Register }
- END;
-
- PROCEDURE ShowHelp;
- BEGIN
- Writeln('>>>IntTerm by Jeff Duntemann >>>>>>>>>>>>>>>>>>>>>>>>>>>');
- Writeln(' Defaults to 1200 Baud; to run at 300 Baud');
- Writeln(' invoke with "300" after "INTTERM" on the command line.');
- Writeln;
- Writeln(' Commands:');
- Writeln(' ALT-X: Exits to DOS');
- Writeln(' CTRL-Z: Clears the screen');
- Writeln('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<');
- END;
-
- {>>>>>INTTERM MAIN PROGRAM<<<<<}
- BEGIN
- HiBaud := True; { Defaults to 1200 baud; if "300" is }
- Divisor := BAUD1200; { entered after "INTTERM" on the command }
- IF ParamCount > 0 THEN { line, then 300 baud is used instead. }
- IF ParamStr(1) = '300' THEN
- BEGIN
- HiBaud := False;
- Divisor := BAUD300
- END;
- DirectVideo := True;
- NoShow := [#0,#127]; { Don't display NUL or RUBOUT }
- SetupSerialPort; { Set up serial port & turn on interrupts }
- ClrScr;
- Writeln('>>>INTTERM by Jeff Duntemann');
- Quit := False; { Exit INTTERM when Quit goes to True }
- REPEAT
- IF InStat THEN { If a character comes in from the modem }
- BEGIN
- CommChar := InChar; { Go get character }
- CommChar := Char(Byte(CommChar) AND $7F); { Mask off high bit }
- IF NOT (CommChar IN NoShow) THEN { If we can show it,}
- Write(CommChar) { then show it! }
- END;
- IF KeyPressed THEN { If a character is typed at the keyboard }
- BEGIN
- KeyChar := ReadKey; { First, read the keystroke }
- IF KeyChar = Chr(0) THEN { We have an extended scan code here }
- BEGIN
- KeyChar := ReadKey; { Read second half of extended code }
- CASE Ord(KeyChar) OF
- 59 : ShowHelp; { F1 : Display help screen }
- 45 : Quit := True; { Alt-X: Exit IntTerm }
- END { CASE }
- END
- ELSE
- CASE Ord(KeyChar) OF
- 26 : ClrScr; { Ctrl-Z: Clear the screen }
- ELSE OutChar(KeyChar)
- END; { CASE }
- END
- UNTIL Quit
- END.